package com.mspbots.ticketai.graderule;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mspbots.ticketai.GradeRuleInterceptorAdapter;
import com.mspbots.ticketai.model.Ticket;
import com.mspbots.ticketai.model.TicketRule;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/12/30
 * <p>
 * CUSTOM_GRADE_POINT_RULE
 * fieldName,fieldType,filterType,value,point,selected
 * datetime,String,boolean,number
 */

@Slf4j
@Service(value = "CUSTOM_FIELD_GRADE_POINT_RULE")
public class CustomFieldGradeRuleInterceptor extends GradeRuleInterceptorAdapter {
    private List<CustomFieldParam> customFieldParams;

    /**
     * 解析jsonNode的值
     *
     * @param node  JsonNode
     * @param attrs "aa.bb.cc"
     */
    public static JsonNode getJsonNodeValue(JsonNode node, String attrs) {
        int index = attrs.indexOf('.');
        if (index == -1) {
            if (node != null && node.get(attrs) != null) {
                return node.get(attrs);
            }
            return null;
        } else {
            return getJsonNodeValue(node.get(attrs.substring(0, index)), attrs.substring(index + 1));
        }
    }

    @Override
    public void prepare(TicketRule rule) {
        this.customFieldParams = this.objectMapper.convertValue(rule.getParams(), new TypeReference<>() {
        });
    }

    @Override
    public void process(Ticket ticket) {
        customFieldParams.forEach(item -> {
            if (item.selected) {
                switch (item.getFilterType()) {
                    case "equal":
                        processEqual(ticket, item);
                        break;
                    case "no_equal":
                        processNoEqual(ticket, item);
                        break;
                    case "greater_than":
                        processGreater(ticket, item);
                        break;
                    case "greater_than_or_equal":
                        processGreaterEqual(ticket, item);
                        break;
                    case "less_than":
                        processLess(ticket, item);
                        break;
                    case "less_than_or_equal":
                        processLessEqual(ticket, item);
                        break;
                    case "contains":
                        processContains(ticket, item);
                        break;
                    case "no_contains":
                        processNoContains(ticket, item);
                        break;
                    default:
                        break;
                }
            }
        });
    }

    private void processEqual(Ticket ticket, CustomFieldParam item) {
        int order = 0;
        JsonNode node = getJsonNodeValue(ticket.getExtend(), item.getFieldName());
        if (node == null || StringUtils.isEmpty(node.asText())) {
            return;
        }
        if ("String".equals(item.getFieldType())) {
            String value = item.getValue().toString();
            if (value.equalsIgnoreCase(node.asText())) {
                order = item.getPoint();
            }
        } else if ("boolean".equals(item.getFieldType())) {
            Boolean value = Boolean.valueOf(item.getValue().toString());
            if (value.equals(node.asBoolean())) {
                order = item.getPoint();
            }
        } else if ("number".equals(item.getFieldType())) {
            Double value = Double.valueOf(item.getValue().toString());
            if (value.equals(node.asDouble())) {
                order = item.getPoint();
            }
        } else if ("datetime".equals(item.getFieldType())) {
            Integer value = Integer.valueOf(item.getValue().toString());
            LocalDateTime nodeTime = LocalDateTime.parse(node.asText(), DateTimeFormatter.ISO_DATE_TIME);
            if (value.equals(Duration.between(nodeTime, LocalDateTime.now()).toMinutes())) {
                order = item.getPoint();
            }
        }
        setTicketDetail(ticket, item, order);
    }

    private void processNoEqual(Ticket ticket, CustomFieldParam item) {
        int order = 0;
        JsonNode node = getJsonNodeValue(ticket.getExtend(), item.getFieldName());
        if (node == null || StringUtils.isEmpty(node.asText())) {
            return;
        }
        if ("String".equals(item.getFieldType())) {
            String value = item.getValue().toString();
            if (!value.equalsIgnoreCase(node.asText())) {
                order = item.getPoint();
            }
        } else if ("boolean".equals(item.getFieldType())) {
            Boolean value = Boolean.valueOf(item.getValue().toString());
            if (!value.equals(node.asBoolean())) {
                order = item.getPoint();
            }
        } else if ("number".equals(item.getFieldType())) {
            Double value = Double.valueOf(item.getValue().toString());
            if (!value.equals(node.asDouble())) {
                order = item.getPoint();
            }
        } else if ("datetime".equals(item.getFieldType())) {
            Integer value = Integer.valueOf(item.getValue().toString());
            LocalDateTime nodeTime = LocalDateTime.parse(node.asText(), DateTimeFormatter.ISO_DATE_TIME);
            if (!value.equals(Duration.between(nodeTime, LocalDateTime.now()).toMinutes())) {
                order = item.getPoint();
            }
        }
        setTicketDetail(ticket, item, order);
    }

    private void processGreater(Ticket ticket, CustomFieldParam item) {
        int order = 0;
        JsonNode node = getJsonNodeValue(ticket.getExtend(), item.getFieldName());
        if (node == null || StringUtils.isEmpty(node.asText())) {
            return;
        }
        if ("String".equals(item.getFieldType())) {
            String value = item.getValue().toString();
            if (value.compareTo(node.asText()) < 0) {
                order = item.getPoint();
            }
        } else if ("number".equals(item.getFieldType())) {
            Double value = Double.valueOf(item.getValue().toString());
            if (value.compareTo(node.asDouble()) < 0) {
                order = item.getPoint();
            }
        } else if ("datetime".equals(item.getFieldType())) {
            Integer value = Integer.valueOf(item.getValue().toString());
            LocalDateTime nodeTime = LocalDateTime.parse(node.asText(), DateTimeFormatter.ISO_DATE_TIME);
            if (value.compareTo(Integer.parseInt(Long.valueOf(Duration.between(nodeTime, LocalDateTime.now()).toMinutes()).toString())) < 0) {
                order = item.getPoint();
            }
        }
        setTicketDetail(ticket, item, order);
    }

    private void processGreaterEqual(Ticket ticket, CustomFieldParam item) {
        int order = 0;
        JsonNode node = getJsonNodeValue(ticket.getExtend(), item.getFieldName());
        if (node == null || StringUtils.isEmpty(node.asText())) {
            return;
        }
        if ("String".equals(item.getFieldType())) {
            String value = item.getValue().toString();
            if (value.compareTo(node.asText()) <= 0) {
                order = item.getPoint();
            }
        } else if ("number".equals(item.getFieldType())) {
            Double value = Double.valueOf(item.getValue().toString());
            if (value.compareTo(node.asDouble()) <= 0) {
                order = item.getPoint();
            }
        } else if ("datetime".equals(item.getFieldType())) {
            Integer value = Integer.valueOf(item.getValue().toString());
            LocalDateTime nodeTime = LocalDateTime.parse(node.asText(), DateTimeFormatter.ISO_DATE_TIME);
            if (value.compareTo(Integer.parseInt(Long.valueOf(Duration.between(nodeTime, LocalDateTime.now()).toMinutes()).toString())) <= 0) {
                order = item.getPoint();
            }
        }
        setTicketDetail(ticket, item, order);
    }

    private void processLess(Ticket ticket, CustomFieldParam item) {
        int order = 0;
        JsonNode node = getJsonNodeValue(ticket.getExtend(), item.getFieldName());
        if (node == null || StringUtils.isEmpty(node.asText())) {
            return;
        }
        if ("String".equals(item.getFieldType())) {
            String value = item.getValue().toString();
            if (value.compareTo(node.asText()) > 0) {
                order = item.getPoint();
            }
        } else if ("number".equals(item.getFieldType())) {
            Double value = Double.valueOf(item.getValue().toString());
            if (value.compareTo(node.asDouble()) > 0) {
                order = item.getPoint();
            }
        } else if ("datetime".equals(item.getFieldType())) {
            Integer value = Integer.valueOf(item.getValue().toString());
            LocalDateTime nodeTime = LocalDateTime.parse(node.asText(), DateTimeFormatter.ISO_DATE_TIME);
            if (value.compareTo(Integer.parseInt(Long.valueOf(Duration.between(nodeTime, LocalDateTime.now()).toMinutes()).toString())) > 0) {
                order = item.getPoint();
            }
        }
        setTicketDetail(ticket, item, order);
    }

    private void processLessEqual(Ticket ticket, CustomFieldParam item) {
        int order = 0;
        JsonNode node = getJsonNodeValue(ticket.getExtend(), item.getFieldName());
        if (node == null || StringUtils.isEmpty(node.asText())) {
            return;
        }
        if ("String".equals(item.getFieldType())) {
            String value = item.getValue().toString();
            if (value.compareTo(node.asText()) >= 0) {
                order = item.getPoint();
            }
        } else if ("number".equals(item.getFieldType())) {
            Double value = Double.valueOf(item.getValue().toString());
            if (value.compareTo(node.asDouble()) >= 0) {
                order = item.getPoint();
            }
        } else if ("datetime".equals(item.getFieldType())) {
            Integer value = Integer.valueOf(item.getValue().toString());
            LocalDateTime nodeTime = LocalDateTime.parse(node.asText(), DateTimeFormatter.ISO_DATE_TIME);
            if (value.compareTo(Integer.parseInt(Long.valueOf(Duration.between(nodeTime, LocalDateTime.now()).toMinutes()).toString())) >= 0) {
                order = item.getPoint();
            }
        }
        setTicketDetail(ticket, item, order);
    }

    private void processContains(Ticket ticket, CustomFieldParam item) {
        int order = 0;
        JsonNode node = getJsonNodeValue(ticket.getExtend(), item.getFieldName());
        if (node == null || StringUtils.isEmpty(node.asText())) {
            return;
        }
        if ("String".equals(item.getFieldType())) {
            String value = item.getValue().toString();
            if (node.asText().toLowerCase().contains(value.toLowerCase())) {
                order = item.getPoint();
            }
        }
        setTicketDetail(ticket, item, order);
    }

    private void processNoContains(Ticket ticket, CustomFieldParam item) {
        int order = 0;
        JsonNode node = getJsonNodeValue(ticket.getExtend(), item.getFieldName());
        if (node == null || StringUtils.isEmpty(node.asText())) {
            return;
        }
        if ("String".equals(item.getFieldType())) {
            String value = item.getValue().toString();
            if (!node.asText().toLowerCase().contains(value.toLowerCase())) {
                order = item.getPoint();
            }
        }
        setTicketDetail(ticket, item, order);
    }

    private void setTicketDetail(Ticket ticket, CustomFieldParam item, int order) {
        if (order != 0) {
            ObjectNode objectNode = ticket.getOrderDetail() == null ? objectMapper.createObjectNode() : ticket.getOrderDetail().deepCopy();
            ObjectNode jsonNodeParams = objectMapper.createObjectNode();
            jsonNodeParams.put("fieldName", item.getFieldName());
            jsonNodeParams.put("fieldType", item.getFieldType());
            jsonNodeParams.put("filterType", item.getFilterType());
            jsonNodeParams.put("value", item.getValue().toString());
            jsonNodeParams.put("point", order);
            if (ticket.getOrderDetail() != null) {
                objectNode.putPOJO("customField", objectNode.get("custom").asText() + "," + jsonNodeParams);
            } else {
                objectNode.putPOJO("customField", jsonNodeParams);
            }
            ticket.setOrderDetail(objectNode);
            ticket.setOrder(ticket.getOrder() + order);
        }
    }

    @Data
    private static class CustomFieldParam {
        private String fieldName;
        private String fieldType;
        private String filterType;
        private Object value;
        private int point;
        private boolean selected;
    }
}
